package net.sparrow.data;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import net.minecraft.src.Minecraft;
import net.sparrow.Sparrow;
import net.sparrow.util.Util;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

public class UtilFile {


	public static PrintStream fileOut;
	
	public static void loadKeybinds() {
		try {
			JSONObject keybinds = loadJSONFile("SparrowKeybinds.json");
			for(Object id : keybinds.keySet()) {
				if (!(id instanceof String)) {
					Util.debug("Found malformed keybind id (" + id.toString() + "), ignoring");
					continue;
				}
				Object command = keybinds.get(id);
				if (!(command instanceof String)) {
					Util.debug("Found malformed command (" + command.toString() + "), ignoring.");
				}
				
				Sparrow.data().addKeybind((String)id, (String)command);
				
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void saveKeybinds() {
		JSONObject data = new JSONObject();
		for(String s : Sparrow.data().getKeybindKeys()) {
			data.put(s, Sparrow.data().getKeybindCommand(s));
		}
		saveJSONFile(data, "SparrowKeybinds.json");
	}

	public static void loadData() {
		try {
			JSONObject data = loadJSONFile("SparrowData.json");
			for(Object id : data.keySet()) {
				Object value = data.get(id);
				if (value instanceof Long) {
					value = ((Long)value).intValue();
				}
				if(value instanceof JSONArray) {
					List<Object> realList = new ArrayList();
					realList.addAll((JSONArray)value);
					value = realList;
				}
				Util.debug("Setting ID: " + id + " to:" + value);
				Sparrow.data().setData((String)id, value);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void saveData() {
		JSONObject data = new JSONObject();
		for(String s : Sparrow.data().getAllDataIds()) {
			data.put(s, Sparrow.data().getData(s));
			Util.debug("Saved Data ID: " + s + "," + Sparrow.data().getData(s));
		}
		saveJSONFile(data, "SparrowData.json");
	}

	//based on http://www.mkyong.com/java/json-simple-example-read-and-write-json/
	public static JSONObject loadJSONFile(String location) {
		JSONParser parser = new JSONParser();
		String baseFilePath = Minecraft.getMinecraft().mcDataDir.getAbsolutePath();
		baseFilePath = baseFilePath.substring(0, baseFilePath.length() - 1); //ends with '.' which is dumb
		File source = new File(baseFilePath + "sparrow/" + location);
		if (!source.getParentFile().exists()) {
			source.getParentFile().mkdirs();
			return new JSONObject();
		}
		Object obj;
		try {
			obj = parser.parse(new FileReader(source));
			return (JSONObject) obj;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return new JSONObject();
	}

	public static void saveJSONFile(JSONObject obj, String location) {
		try {
			String baseFilePath = Minecraft.getMinecraft().mcDataDir.getAbsolutePath();
			baseFilePath = baseFilePath.substring(0, baseFilePath.length() - 1); //ends with '.' which is dumb
			File destination = new File(baseFilePath + "sparrow/" + location);
			if (!destination.getParentFile().exists()) {
				destination.getParentFile().mkdirs();
			}
			FileWriter file = new FileWriter(destination);
			file.write(obj.toJSONString());
			file.flush();
			file.close();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static boolean isClassJSONCompatible(Class c) {
		if (c.isAssignableFrom(String.class)) {
			return true;
		}
		if (c.isAssignableFrom(Double.class)) {
			return true;
		}
		if (c.isAssignableFrom(Boolean.class)) {
			return true;
		}
		if (c.isAssignableFrom(Integer.class)) {
			return true;
		}
		if (c.isAssignableFrom(ArrayList.class)) {
			return true;
		}
		return false;
	}

	//thanks to https://github.com/ddopson/java-class-enumerator for this awesome method
	public static ArrayList<Class<?>> getClassesForPackage(Package pkg) {
		String pkgname = pkg.getName();
		ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
		// Get a File object for the package
		File directory = null;
		String fullPath;
		String relPath = pkgname.replace('.', '/');
		URL resource = ClassLoader.getSystemClassLoader().getResource(relPath);
		if (resource == null) {
			throw new RuntimeException("No resource for " + relPath);
		}
		fullPath = resource.getFile();
		System.out.println("ClassDiscovery: FullPath = " + resource);

		try {
			directory = new File(resource.toURI());
		} catch (URISyntaxException e) {
			throw new RuntimeException(pkgname + " (" + resource + ") does not appear to be a valid URL / URI.  Strange, since we got it from the system...", e);
		} catch (IllegalArgumentException e) {
			directory = null;
		}

		if (directory != null && directory.exists()) {
			// Get the list of the files contained in the package
			String[] files = directory.list();
			for (int i = 0; i < files.length; i++) {
				// we are only interested in .class files
				if (files[i].endsWith(".class")) {
					// removes the .class extension
					String className = pkgname + '.' + files[i].substring(0, files[i].length() - 6);
					try {
						classes.add(Class.forName(className));
					} 
					catch (ClassNotFoundException e) {
						throw new RuntimeException("ClassNotFoundException loading " + className);
					}
				}
			}
		}
		else {
			try {
				String jarPath = fullPath.replaceFirst("[.]jar[!].*", ".jar").replaceFirst("file:", "");
				JarFile jarFile = new JarFile(jarPath);         
				Enumeration<JarEntry> entries = jarFile.entries();
				while(entries.hasMoreElements()) {
					JarEntry entry = entries.nextElement();
					String entryName = entry.getName();
					if(entryName.startsWith(relPath) && entryName.length() > (relPath.length() + "/".length())) {
						System.out.println("ClassDiscovery: JarEntry: " + entryName);
						String className = entryName.replace('/', '.').replace('\\', '.').replace(".class", "");
						System.out.println("ClassDiscovery: className = " + className);
						try {
							classes.add(Class.forName(className));
						} 
						catch (ClassNotFoundException e) {
							throw new RuntimeException("ClassNotFoundException loading " + className);
						}
					}
				}
			} catch (IOException e) {
				throw new RuntimeException(pkgname + " (" + directory + ") does not appear to be a valid package", e);
			}
		}
		return classes;
	}

	public static void setupPrintFile() {
		String baseFilePath = Minecraft.getMinecraft().mcDataDir.getAbsolutePath();
		baseFilePath = baseFilePath.substring(0, baseFilePath.length() - 1); //ends with '.' which is dumb
		File destination = new File(baseFilePath + "sparrow/sparrow-log.log");
		if(destination.exists()) {
			destination.delete(); //delete last log file
		} else {
			destination.getParentFile().mkdirs();
		}
		try {
			fileOut = new PrintStream(new BufferedOutputStream(new FileOutputStream(destination)));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	public static void closePrintFile() {
		fileOut.println("Sparrow Client has shut down.");
		fileOut.close();
	}
}
